using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Reflection;
using System.Web;
using System.Xml;
using Castle.ActiveRecord;
using Castle.ActiveRecord.Framework.Internal;
using NHibernate.Criterion;
using Pampa.CRUD.Core;
using Pampa.CRUD.Exceptions;
using Pampa.MVC.Models;

namespace Pampa.MVC.Data
{
    public class EntityRepositoryCastle : IEntityRepository
    {
        public bool IsEntity(Type type)
        {
            if (typeof(IPampaViewModel).IsAssignableFrom(type))
            {
                var entityType = ((IPampaViewModel)type).GetInnerEntity().GetType();
                var model = ActiveRecordModel.GetModel(entityType);
                return (model != null);
            }
            else
            {
                var model = ActiveRecordModel.GetModel(type);
                return (model != null);
            }
        }
        
        public virtual bool IsNew(object entity)
        {
            if (entity is ICustomPersistence)
            {
                return ((ICustomPersistence)entity).IsNew;
            }
            else
            {
                var pk = ActiveRecordModel.GetModel(entity.GetType()).PrimaryKey;
                var pkValue = pk.Property.GetValue(entity, null);
                var pkType = pk.Property.PropertyType;
                var defaultValue = Activator.CreateInstance(pkType);

                var transient = Equals(defaultValue, pkValue);
                return transient;
            }
        }

        public virtual void Delete(object entity)
        {
            if (entity is ICustomPersistence)
            {
                ((ICustomPersistence)entity).CustomDelete();
            }
            else
            {
                ActiveRecordMediator.Delete(entity);
            }
        }

        public ITransaction BeginTransaction(object entity)
        {
            return new TransactionCastle();
        }

        public void Save(object entity)
        {
            // easter egg -->
            try
            {
                string file = HttpContext.Current.Server.MapPath("\\App_Data\\version.txt");
                string versionNumber = System.IO.File.ReadAllText(file);
                System.IO.File.WriteAllText(file, versionNumber + " ...");
            }
            catch (Exception)
            {
            }
            // <--

            if (entity is ICustomPersistence)
            {
                var pampaEntity = ((ICustomPersistence)entity);
                pampaEntity.Validate();
                pampaEntity.CustomSave();
            }
            else
            {
                ActiveRecordMediator.Save(entity);
            }
        }

        public virtual object Find(Type type, object id)
        {
            if (typeof(IPampaViewModel).IsAssignableFrom(type))
            {
                var entityType = ((IPampaViewModel)Activator.CreateInstance(type)).GetInnerEntity().GetType();
                var entity = ActiveRecordMediator.FindByPrimaryKey(entityType, id);
                var result = (IPampaViewModel)Activator.CreateInstance(type);
                result.SetInnerEntity(entity);
                return result;
            }
            else
            {
                return ActiveRecordMediator.FindByPrimaryKey(type, id);
            }
        }

        public virtual string GetId(object entity)
        {
            object resolvedEntity = entity;

            if (entity is IPampaViewModel)
            {
                resolvedEntity = ((IPampaViewModel)entity).GetInnerEntity();
            }

            var pk = ActiveRecordModel.GetModel(resolvedEntity.GetType()).PrimaryKey;
            var pkValue = pk.Property.GetValue(resolvedEntity, null);
            return pkValue.ToString();
        }

        public virtual object ParseId(Type type, string strId)
        {
            var entityType = ResolveEntityType(type);
            var pk = ActiveRecordModel.GetModel(entityType).PrimaryKey;
            var pkType = pk.Property.PropertyType;
            return Convert.ChangeType(strId, pkType);
        }

        public virtual IList GetList(string entityQualifiedName)
        {
            var type = GeTEditorType(entityQualifiedName);
            var criterias = DefaultCriterias(type);
            return ActiveRecordMediator.FindAll(type, criterias.ToArray());
        }

        public virtual IList GetList(string entityQualifiedName, string orderBy)
        {
            var type = GeTEditorType(entityQualifiedName);
            var criterias = DefaultCriterias(type);
            return ActiveRecordMediator.FindAll(type, new Order[] { new Order(orderBy, false), }, criterias.ToArray());
        }

        public virtual IList GetList(string entityQualifiedName, string property, string strPropertyValue)
        {
            var type = GeTEditorType(entityQualifiedName);
            var propertyValue = EntityManager.GetPropertyValueFromString(type, property, strPropertyValue);

            var criterias = DefaultCriterias(type);
            criterias.Add(Expression.Eq(property, propertyValue));

            return ActiveRecordMediator.FindAll(type, criterias.ToArray());
        }

        public virtual IList GetList(string entityQualifiedName, string property, string strPropertyValue, string orderBy)
        {
            var type = GeTEditorType(entityQualifiedName);
            var propertyValue = EntityManager.GetPropertyValueFromString(type, property, strPropertyValue);

            var criterias = DefaultCriterias(type);
            criterias.Add(Expression.Eq(property, propertyValue));

            return ActiveRecordMediator.FindAll(type, new Order[] { new Order(orderBy, false), }, criterias.ToArray());
        }

        public virtual IList GetList(string entityQualifiedName, string likeProperty, string likeValue, int maxresults)
        {
            var type = GeTEditorType(entityQualifiedName);
            var order = new Order[] { Order.Asc(likeProperty) };

            var criterias = DefaultCriterias(type);
            criterias.Add(Expression.Like(likeProperty, likeValue, MatchMode.Anywhere));

            return ActiveRecordMediator.SlicedFindAll(type, 0, maxresults, order, criterias.ToArray());
        }

        public virtual IList GetList(string entityQualifiedName, string likeProperty, string likeValue, int maxresults, string filterProperty, string strFilterPropertyValue)
        {
            var type = GeTEditorType(entityQualifiedName);
            var order = new Order[] { Order.Asc(likeProperty) };
            object filterPropertyValue = EntityManager.GetPropertyValueFromString(entityQualifiedName, filterProperty, strFilterPropertyValue);

            var criterias = DefaultCriterias(type);
            criterias.Add(Expression.Eq(filterProperty, filterPropertyValue));
            criterias.Add(Expression.Like(likeProperty, likeValue, MatchMode.Anywhere));

            return ActiveRecordMediator.SlicedFindAll(type, 0, maxresults, order, criterias.ToArray());
        }

        #region Private Functions

        private Type GeTEditorType(string entityQualifiedName)
        {
            var type = Type.GetType(entityQualifiedName);
            return ResolveEntityType(type);

        }

        private Type ResolveEntityType(Type type)
        {
            if (typeof(IPampaViewModel).IsAssignableFrom(type))
            {
                return ((IPampaViewModel)Activator.CreateInstance(type)).GetInnerEntity().GetType();
            }
            else
            {
                return type;
            }
        }

        private List<ICriterion> DefaultCriterias(Type type)
        {
            if (typeof(ICustomPersistence).IsAssignableFrom(type))
            {
                return new List<ICriterion> { Expression.Eq("Visible", true) };
            }
            else
            {
                return new List<ICriterion>();
            }
        }

        #endregion
    }
}
